package com.salinity.kun.helper;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.salinity.kun.Constant.OUTPUT_ITEM;
import com.salinity.kun.algorithm.harmonic.ConstantParamHelper;
import com.salinity.kun.model.FourierSeriesInput;
import com.salinity.kun.model.HydrologicalData;
import com.salinity.kun.model.HydrologicalDataList;
import com.salinity.kun.model.HydrologicalDataList.DataItem;
import com.salinity.kun.util.PathUtil;
import com.salinity.kun.util.StringArrayUtil;

public class CalculationHelper {

	public static HydrologicalDataList getDataByDateFromFile(Date beginTime, Date endTime, String stationName) {

		Path rawDataAvgPath = getRawDataAvgPath(stationName);

		if (rawDataAvgPath.toFile().exists()) {
			// date format of date in raw data
			SimpleDateFormat defaultSdf = ConstantHelper.getDefaultFormat();
			List<HydrologicalData> hydrologicalDataList = new ArrayList<>();
			try (BufferedReader br = Files.newBufferedReader(rawDataAvgPath)) {

				// skip description
				String line = br.readLine();

				while ((line = br.readLine()) != null) {
					String[] strArray = line.split(",");
					if (strArray.length >= 4) {

						String dateStr = strArray[0];

						// If current date is in this range
						if (defaultSdf.parse(dateStr).compareTo(beginTime) >= 0) {
							if (defaultSdf.parse(dateStr).compareTo(endTime) <= 0) {
								hydrologicalDataList.add(new HydrologicalData(dateStr, Double.parseDouble(strArray[1]),
										Double.parseDouble(strArray[2]), Double.parseDouble(strArray[3])));
							} else {
								// Exceed the end date
								break;
							}
						}
					}
				}

				return new HydrologicalDataList(hydrologicalDataList);
			} catch (IOException e) {
				System.out.println("read file error.");
				throw new RuntimeException(e);
			} catch (ParseException e) {
				System.out.println("Can not parse this date.");
				throw new RuntimeException(e);
			} catch (NumberFormatException e) {
				System.out.println("Can not parse string to double.");
				throw new RuntimeException(e);
			}
		}

		return null;
	}

	public static HydrologicalDataList getDataByDateFromDB(Date beginTime, Date endTime, String stationId) {

		// TO DO
		return null;
	}

	public static Path getRawDataAvgPath(String stationName) {
		String[] stationNames = ConstantHelper.getStationNameArray();
		if (StringArrayUtil.Contains(stationNames, stationName)) {
			Path commonDataPath = ConstantHelper.getCommonDataPath();
			String rawDataAvgFileNamePrefix = ConstantHelper.getRawDataAvgFileNamePrefix();
			return commonDataPath.resolve(rawDataAvgFileNamePrefix + stationName + ".csv").normalize();
		} else {
			return null;
		}
	}

	public static Path[] getRawDataAvgPathArray() {
		String[] stationNames = ConstantHelper.getStationNameArray();
		Path commonDataPath = ConstantHelper.getCommonDataPath();
		String rawDataAvgFileNamePrefix = ConstantHelper.getRawDataAvgFileNamePrefix();
		Path[] rawDataAvgPathArray = new Path[stationNames.length];
		for (int i = 0; i < rawDataAvgPathArray.length; i++) {
			rawDataAvgPathArray[i] = commonDataPath.resolve(rawDataAvgFileNamePrefix + stationNames[i] + ".csv")
					.normalize();
		}
		return rawDataAvgPathArray;
	}

	public static Path getRawDataPathArray(String stationName) {
		String[] stationNames = ConstantHelper.getStationNameArray();

		if (StringArrayUtil.Contains(stationNames, stationName)) {
			Path commonDataPath = ConstantHelper.getCommonDataPath();
			String rawDataFileNamePrefix = ConstantHelper.getRawDataFileNamePrefix();
			return commonDataPath.resolve(rawDataFileNamePrefix + stationName + ".csv").normalize();
		} else {
			return null;
		}

	}

	public static Path[] getRawDataPathArray() {
		String[] stationNames = ConstantHelper.getStationNameArray();
		Path commonDataPath = ConstantHelper.getCommonDataPath();
		String rawDataFileNamePrefix = ConstantHelper.getRawDataFileNamePrefix();
		Path[] rawDataPathArray = new Path[stationNames.length];
		for (int i = 0; i < rawDataPathArray.length; i++) {
			rawDataPathArray[i] = commonDataPath.resolve(rawDataFileNamePrefix + stationNames[i] + ".csv").normalize();
		}
		return rawDataPathArray;
	}

	public static Map<DataItem, String> getNamesMap() {
		String[] Names = ConstantHelper.getFtNames();
		Map<DataItem, String> namesMap = new HashMap<>();
		int i = 0;
		for (DataItem dataItem : DataItem.values()) {
			if (dataItem.equals(DataItem.DATE))
				continue;
			if (i < Names.length) {
				namesMap.put(dataItem, Names[i++]);
			} else {
				break;
			}

		}
		return namesMap;
	}

	public static Path getResultPath(String stationName, String resultFileName) {
		String[] stationNames = ConstantHelper.getStationNameArray();
		if (StringArrayUtil.Contains(stationNames, stationName)) {
			Path resultPath = ConstantHelper.getResultPath();
			return resultPath.resolve(stationName).resolve(stationName + "@" + resultFileName + ".csv").normalize();
		} else {
			return null;
		}
	}

	public static void writeToFile(FourierSeriesInput fourierSeriesInput, String[] itemArray, Path outputPath)
			throws IOException {

		writeToFile(fourierSeriesInput, itemArray, outputPath, true, fourierSeriesInput.getF().length-1);
	}

	public static void writeToFile(FourierSeriesInput fourierSeriesInput, String[] itemArray, Path outputPath,
			boolean from_a0) throws IOException {

		writeToFile(fourierSeriesInput, itemArray, outputPath, from_a0, fourierSeriesInput.getF().length-1);
	}

	// 写入文件
	public static void writeToFile(FourierSeriesInput fourierSeriesInput, String[] itemArray, Path outputPath,
			boolean from_a0, int ftCalCount) throws IOException {
		
		FourierSeriesHelper fsHelper = new FourierSeriesHelper(fourierSeriesInput);

		
		// if exist,delete

		PathUtil.preparePath(outputPath);
		
		//get data format 
		DecimalFormat df = ConstantParamHelper.getDefaultDecimalFormat();

		// write data to file
		try (BufferedWriter bw = Files.newBufferedWriter(outputPath)) {

			StringBuilder sb = new StringBuilder();
			int itemIndex;
			bw.write("name," + String.valueOf(fourierSeriesInput.getName()));
			bw.newLine();
			bw.write("t," + String.valueOf(fourierSeriesInput.getT()));
			bw.newLine();
			bw.write("dt," + String.valueOf(fourierSeriesInput.getDeltaT()));
			bw.newLine();
			bw.write("sn," + String.valueOf(fourierSeriesInput.getSeriesCount()));
			bw.newLine();
			for (itemIndex = 0; itemIndex < itemArray.length; itemIndex++) {
				if (itemArray[itemIndex].equals(OUTPUT_ITEM.p.toString())
						|| itemArray[itemIndex].equals(OUTPUT_ITEM.ip.name())) {
					sb.append(fourierSeriesInput.getName() + "_" + itemArray[itemIndex]);
				} else if (itemArray[itemIndex].equals(OUTPUT_ITEM.ft_cal.toString())) {
					sb.append(fourierSeriesInput.getName() + "_cal_ft");
				} else {
					sb.append(itemArray[itemIndex]);
				}

				if (itemIndex < itemArray.length - 1) {
					sb.append(",");
				}
			}
			bw.write(sb.toString());
			bw.newLine();

			double ai = 0.0, bi = 0.0;
			boolean outSeriesCount = false;
			int i = 0;
			for (; i < fourierSeriesInput.getF().length; i++) {
				if (i <= fourierSeriesInput.getSeriesCount()) {
					ai = fsHelper.get_a(i);
					bi = fsHelper.get_b(i);
				} else if (!outSeriesCount) {
					outSeriesCount = true;
				}
				if (!from_a0 && i == 0) {
					continue;
				}
				sb.delete(0, sb.length());
				for (itemIndex = 0; itemIndex < itemArray.length; itemIndex++) {
					switch (OUTPUT_ITEM.valueOf(itemArray[itemIndex])) {
					case ai:
						if (!outSeriesCount) {
							sb.append(df.format(ai));
						}
						break;
					case bi:
						if (!outSeriesCount) {
							sb.append(df.format(bi));
						}
						break;
					case iw:
						sb.append(df.format(i * (2f * Math.PI) / fourierSeriesInput.getT()));
						break;
					case p:
						if (!outSeriesCount) {
							sb.append(df.format(fsHelper.get_fs(i)));
						}
						break;
					case si:
						if (!outSeriesCount) {
							sb.append(String.valueOf(i));
						}
						break;
					case ti:
						if (!outSeriesCount) {
							sb.append(df.format(fsHelper.get_theta_i(ai, bi)));
						}
						break;
					case ip:
						if (!outSeriesCount) {
							sb.append(df.format(fsHelper.getInitialPhase(ai, bi)));
						}
						break;
					case ft:
						sb.append(df.format(fourierSeriesInput.getF()[i]));
						break;
					case ft_cal:
						if (ftCalCount > 0 && i <= ftCalCount) {
							sb.append(df.format(fsHelper.getFtCalByIndex(i)));
						}
						break;
					default:
						break;
					}
					if (itemIndex < itemArray.length - 1) {
						sb.append(",");
					}
				}
				bw.write(sb.toString());
				bw.newLine();
			}

			// 用于验证多于ft数目的正确性
			while (i <= ftCalCount) {
				sb.delete(0, sb.length());
				for (itemIndex = 0; itemIndex < itemArray.length; itemIndex++) {
					switch (OUTPUT_ITEM.valueOf(itemArray[itemIndex])) {
					case ft_cal:
						sb.append(df.format(fsHelper.getFtCalByIndex(i)));
						break;
					default:
						break;
					}
					if (itemIndex < itemArray.length - 1) {
						sb.append(",");
					}
				}
				bw.write(sb.toString());
				bw.newLine();
				i++;
			}

		}
	}

}
